/*!
    \file    i2c_slave_0.c
    \brief   Software I2C-Slave Device0 instantiation

    \version 2023-04-10, V1.0.0, init version
    \version 2023-04-12, V1.0.1, Friendly API
*/
#include "i2c_main_core.h"
#include "demo_i2c_device.h"

#include "gd32f1x0.h"
#include "systick.h"
#include <stdio.h>



#define SW_MAIN_SCL_PORT                  GPIOF
#define SW_MAIN_SCL_PIN                   GPIO_PIN_6
#define SW_MAIN_SCL_CLK                   RCU_GPIOF


#define SW_MAIN_SDA_PORT                  GPIOF
#define SW_MAIN_SDA_PIN                   GPIO_PIN_7
#define SW_MAIN_SDA_CLK                   RCU_GPIOF

// 7-bit without R/W Bit
static unsigned char demo_i2c_device_addr  = 0x58;



#define i2c_debug(format,...)
//#define i2c_debug printf


/* --------------------------------------------------------- Private  ----------------------------------------------------------*/
static void i2c2_sda_pin_low(void);
static void i2c2_sda_pin_high(void);
static void i2c2_scl_pin_low(void);
static void i2c2_scl_pin_high(void);
static uint8_t i2c2_sda_pin_read(void);
static void i2c2_sda_pin_dir_input(void);
static void i2c2_sda_pin_dir_output(void);
static void i2c_delay(const uint32_t us);


/* --------------------------------------------------------- Private  --------------------------------------------------------- */

static struct sf_i2c_dev demo_i2c_device = {
    /*.name               = */ "i2c2",
    /*.speed              = */ 2, /*! speed:105Hz */
    /*.delay_us           = */ i2c_delay,
    /*.ops.sda_low        = */ i2c2_sda_pin_low,
    /*.ops.sda_high       = */ i2c2_sda_pin_high,
    /*.ops.scl_low        = */ i2c2_scl_pin_low,
    /*.ops.scl_high       = */ i2c2_scl_pin_high,
    /*.ops.sda_read_level = */ i2c2_sda_pin_read,
    /*.ops.sda_set_input  = */ i2c2_sda_pin_dir_input,
    /*.ops.sda_set_output = */ i2c2_sda_pin_dir_output,
};

/*! Set i2c sda pin low level */
static void i2c2_sda_pin_low(void)
{
    gpio_bit_reset(SW_MAIN_SDA_PORT, SW_MAIN_SDA_PIN);
}

/*! Set i2c sda pin high level */
static void i2c2_sda_pin_high(void)
{
    gpio_bit_set(SW_MAIN_SDA_PORT, SW_MAIN_SDA_PIN);
}

/*! Set i2c scl pin low level */
static void i2c2_scl_pin_low(void)
{
    gpio_bit_reset(SW_MAIN_SCL_PORT, SW_MAIN_SCL_PIN);
}

/*! Set i2c scl pin high level */
static void i2c2_scl_pin_high(void)
{
    gpio_bit_set(SW_MAIN_SCL_PORT, SW_MAIN_SCL_PIN);
}

/*! Read i2c sda pin level */
static uint8_t i2c2_sda_pin_read(void)
{
    return gpio_input_bit_get(SW_MAIN_SDA_PORT, SW_MAIN_SDA_PIN);
}

/*! Switch i2c sda pin dir input */
static void i2c2_sda_pin_dir_input(void)
{
    gpio_mode_set(SW_MAIN_SDA_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SW_MAIN_SDA_PIN);
}

/*! Switch i2c sda pin dir output */
static void i2c2_sda_pin_dir_output(void)
{
    gpio_mode_set(SW_MAIN_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SW_MAIN_SDA_PIN);
}


/**
 * @brief  i2c software delay function, used to control the i2c bus speed
 * @param  us : 1ms = 100us, 1s = 1000ms
 * @return none
 */
static void i2c_delay(const uint32_t us)
{
    delay_1us(us);
}


/* --------------------------------------------------------- API  --------------------------------------------------------- */



/**
 * @brief  Initialization i2c2 physical interface
 * @param  none
 * @return none
 */
static void demo_i2c_device_port_init(void)
{
    /* enable the GPIO, Interrupt clock*/
    rcu_periph_clock_enable(SW_MAIN_SDA_CLK);
    rcu_periph_clock_enable(SW_MAIN_SCL_CLK);

    gpio_mode_set(SW_MAIN_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SW_MAIN_SCL_PIN);
    gpio_output_options_set(SW_MAIN_SCL_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SW_MAIN_SCL_PIN);
    
    gpio_mode_set(SW_MAIN_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,           SW_MAIN_SDA_PIN);
    gpio_output_options_set(SW_MAIN_SDA_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SW_MAIN_SDA_PIN);
}








/**
 * @brief  Set I2C Device address, for `demo_i2c_device write/read`, 
           you can skip this function if you want to use 'demo_i2c_bus write/read' only.
 * @param[in]   DevAddr_7Bit : I2C Device address(7BIT)
 * @return none
 */
void set_demo_i2c_device_addr(unsigned char DevAddr_7Bit)
{
    //I2C2_ADDR = addr>> 1;
    demo_i2c_device_addr = DevAddr_7Bit;
}






/**
 * @brief  Initialization board i2c2 interface
 * @param  none
 * @return none
 */
void demo_i2c_device_init(void)
{
    /*! i2c physical layer initialization */
    demo_i2c_device_port_init();

    /*! i2c software layer initialization */
    sf_i2c_init(&demo_i2c_device);
}




/**
 * @brief  Read register from I2C Device
 * @param[in]   RegAddr : register
 * @param[out]  readVal : read value (if OK).
 * @return I2C_SUCCESS when readVal is set, or I2C_TIMEOUT when I2c Device not ack, or I2C_INVARGS when argument is NULL.
 */
I2C_Error_t demo_i2c_device_read_byte(unsigned char RegAddr, unsigned char  *readVal)
{
	struct sf_i2c_msg msgs[2];

    if(readVal== NULL)
    {
        return I2C_INVARGS;
    }

	// ????????
	msgs[0].addr  = demo_i2c_device_addr;
    msgs[0].flags = SF_I2C_FLAG_WR;
    msgs[0].buf   = &RegAddr;
    msgs[0].len   = 1;
    
    // ???????????????
    msgs[1].addr  = demo_i2c_device_addr;
    msgs[1].flags = SF_I2C_FLAG_RD;
    msgs[1].buf   = readVal;
    msgs[1].len   = 1;
    return sf_i2c_transfer(&demo_i2c_device, msgs, 2);
}

/**
 * @brief  Write register to I2C Device
 * @param[in]   RegAddr : register
 * @param[in]   readVal : write value.
 * @return I2C_SUCCESS when data is set, or I2C_TIMEOUT when I2c Device not ack;
 */
I2C_Error_t demo_i2c_device_write_byte(unsigned char RegAddr, unsigned char data)
{
    unsigned char test_data[1];
	struct sf_i2c_msg msgs[2];
	test_data[0] = data;

	// ????????
	msgs[0].addr  = demo_i2c_device_addr;
    msgs[0].flags = SF_I2C_FLAG_WR;
    msgs[0].buf   = &RegAddr;
    msgs[0].len   = 1;
    
    // ???????????????
    msgs[1].addr  = demo_i2c_device_addr;
    msgs[1].flags = SF_I2C_FLAG_WR | SF_I2C_FLAG_NO_START;
    msgs[1].buf   = test_data;
    msgs[1].len   = 1;

    return sf_i2c_transfer(&demo_i2c_device, msgs, 2);
}






/**
 * @brief  Read register from I2C bus (Linux-Like)
 * @param[in]   DevAddr_7Bit : I2C Device address(7BIT)
 * @param[in]   RegAddr : register
 * @param[out]  readVal : read value (if OK).
 * @return I2C_SUCCESS when readVal is set, or I2C_TIMEOUT when I2c Device not ack, or I2C_INVARGS when argument is NULL.
 */
I2C_Error_t demo_i2c_bus_read_byte(unsigned char DevAddr_7Bit, unsigned char RegAddr, unsigned char  *readVal)
{
	struct sf_i2c_msg msgs[2];

    if(readVal== NULL)
    {
        return I2C_INVARGS;
    }

	// ????????
	msgs[0].addr  = DevAddr_7Bit;
    msgs[0].flags = SF_I2C_FLAG_WR;
    msgs[0].buf   = &RegAddr;
    msgs[0].len   = 1;
    
    // ???????????????
    msgs[1].addr  = DevAddr_7Bit;
    msgs[1].flags = SF_I2C_FLAG_RD;
    msgs[1].buf   = readVal;
    msgs[1].len   = 1;
    return sf_i2c_transfer(&demo_i2c_device, msgs, 2);
}

/**
 * @brief  Write register I2C bus (Linux-Like)
 * @param[in]   DevAddr_7Bit : I2C Device address(7BIT)
 * @param[in]   RegAddr : register
 * @param[in]   readVal : write value.
 * @return I2C_SUCCESS when data is set, or I2C_TIMEOUT when I2c Device not ack;
 */
I2C_Error_t demo_i2c_bus_write_byte(unsigned char DevAddr_7Bit, unsigned char RegAddr, unsigned char data)
{
    unsigned char test_data[1];
	struct sf_i2c_msg msgs[2];
	test_data[0] = data;

	// ????????
	msgs[0].addr  = DevAddr_7Bit;
    msgs[0].flags = SF_I2C_FLAG_WR;
    msgs[0].buf   = &RegAddr;
    msgs[0].len   = 1;
    
    // ???????????????
    msgs[1].addr  = DevAddr_7Bit;
    msgs[1].flags = SF_I2C_FLAG_WR | SF_I2C_FLAG_NO_START;
    msgs[1].buf   = test_data;
    msgs[1].len   = 1;

    return sf_i2c_transfer(&demo_i2c_device, msgs, 2);
}

















/**
 * @brief  Write registers to I2C Device
 * @param[in]   RegAddr : register
 * @param[in]   pData   : write values.
 * @param[in]   length  : the length of pData.
 * @return I2C_SUCCESS when readVal is set, or I2C_TIMEOUT when I2c Device not ack, or I2C_INVARGS when argument is NULL.
 * @return I2C_SUCCESS when data is set, or I2C_TIMEOUT when I2c Device not ack;
 */
unsigned char demo_i2c_device_write_bytes(unsigned char RegAddr, unsigned char *pData, unsigned short length)
{
	unsigned short i = 0;
    unsigned char ok_cnt = 0;

    if(pData == NULL)
    {
        return I2C_INVARGS;
    }
    
	for(i = 0; i < length; i++)
	{
		if(demo_i2c_device_write_byte(RegAddr + i, pData[i]) == I2C_SUCCESS)
        {
            ok_cnt = ok_cnt + 1;
            continue;
        }
        return ok_cnt;
	}
    return ok_cnt;
}

/**
 * @brief  Read registers from I2C Device
 * @param[in]   RegAddr : register
 * @param[in]   pData   : array for saveing read value.
 * @param[in]   length  : the length of pData.
 * @return I2C_SUCCESS when readVal is set, or I2C_TIMEOUT when I2c Device not ack, or I2C_INVARGS when argument is NULL.
 */
unsigned char demo_i2c_device_read_bytes(unsigned char RegAddr, unsigned char *pData, unsigned short length)
{
	unsigned short i = 0;
    unsigned char ok_cnt = 0;

    if(pData == NULL)
    {
        return I2C_INVARGS;
    }

	for(i = 0 ; i < length ; i++)
	{
		 if(demo_i2c_device_read_byte(RegAddr + i, pData + i)== I2C_SUCCESS)
         {
            ok_cnt = ok_cnt + 1;
            continue;
         }
        return ok_cnt;
	}
    return ok_cnt;
}











/**
 * @brief  Write registers to I2C bus (Linux-Like)
 * @param[in]   DevAddr_7Bit : I2C Device address(7BIT)
 * @param[in]   RegAddr : register
 * @param[in]   pData   : write values.
 * @param[in]   length  : the length of pData.
 * @return I2C_SUCCESS when readVal is set, or I2C_TIMEOUT when I2c Device not ack, or I2C_INVARGS when argument is NULL.
 * @return I2C_SUCCESS when data is set, or I2C_TIMEOUT when I2c Device not ack;
 */
unsigned char demo_i2c_bus_write_bytes(unsigned char DevAddr_7Bit, unsigned char DevAddr, unsigned char RegAddr, unsigned char *pData, unsigned short length)
{
	unsigned short i = 0;
    unsigned char ok_cnt = 0;

    if(pData == NULL)
    {
        return I2C_INVARGS;
    }
    
	for(i = 0; i < length; i++)
	{
		if(demo_i2c_bus_write_byte(DevAddr_7Bit, RegAddr + i, pData[i]) == I2C_SUCCESS)
        {
            ok_cnt = ok_cnt + 1;
            continue;
        }
        return ok_cnt;
	}
    return ok_cnt;
}

/**
 * @brief  Read registers from I2C Device
 * @param[in]   DevAddr_7Bit : I2C Device address(7BIT)
 * @param[in]   RegAddr : register
 * @param[out]  pData   : array for saveing read value.
 * @param[in]   length  : the length of pData.
 * @return I2C_SUCCESS when readVal is set, or I2C_TIMEOUT when I2c Device not ack, or I2C_INVARGS when argument is NULL.
 */
unsigned char demo_i2c_bus_read_bytes(unsigned char DevAddr_7Bit, unsigned char RegAddr, unsigned char *pData, unsigned short length)
{
	unsigned short i = 0;
    unsigned char ok_cnt = 0;

    if(pData == NULL)
    {
        return I2C_INVARGS;
    }

	for(i = 0 ; i < length ; i++)
	{
		 if(demo_i2c_bus_read_byte(DevAddr_7Bit, RegAddr + i, pData + i)== I2C_SUCCESS)
         {
            ok_cnt = ok_cnt + 1;
            continue;
         }
        return ok_cnt;
	}
    return ok_cnt;
}
